package it.yuzuojian.com.aop;


import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GPAdviceSupport {
    private Class targetClass;
    private Object target;
    private Map<Method, Map<String, List<GPAdvice>>> methodCache = new HashMap<>();
    private boolean check = false;


    public boolean pointCutMatch() {

        return check;
    }

    public void parse() {
        List<GPAopConfig> configs = AopFactory.getConfigs();
        configs.forEach((config) -> {
            String pointCut_package = config.getPointCut_package();
            if (targetClass.getPackage().getName().contains(pointCut_package)) {
                String[] pointCut_class = config.getPointCut_class();
                String[] pointCut_method = config.getPointCut_method();
                Map<String, Method> aspectMethods = new HashMap<>();
                Class aspectClass = null;
                try {
                    aspectClass = Class.forName(config.getAspectClass());
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                for (Method declaredMethod : aspectClass.getDeclaredMethods()) {
                    aspectMethods.put(declaredMethod.getName(), declaredMethod);
                }
                if (pointCut_class[0].equals("") && pointCut_method[0].equals("")) {
                    if (!check) {
                        check = true;
                    }
                    init(config, aspectClass, aspectMethods);
                } else if (!pointCut_class[0].equals("") && pointCut_method[0].equals("")) {
                    if (targetClass.getSimpleName().equals(pointCut_class)) {
                        if (!check) {
                            check = true;
                        }
                        init(config, aspectClass, aspectMethods);
                    }
                } else if (pointCut_class[0].equals("") && !pointCut_method[0].equals("")) {
                    init(config, aspectClass, aspectMethods, pointCut_method);

                } else {
                    if (!check) {
                        check = true;
                    }
                    init(config, aspectClass, aspectMethods, pointCut_class, pointCut_method);
                }
            }
        });


    }

    public void init(GPAopConfig config, Class aspectClass, Map<String, Method> aspectMethods) {
        for (Method declaredMethod : this.targetClass.getDeclaredMethods()) {
            String methodString = declaredMethod.getName();
            if ("wait".equals(methodString) || "equals".equals(methodString) || "toString".equals(methodString) ||
                    "hashCode".equals(methodString) || "getClass".equals(methodString) || "notify".equals(methodString) ||
                    "notifyAll".equals(methodString)) {
                continue;
            }
            Map<String, List<GPAdvice>> advices = methodCache.get(declaredMethod);
            if (advices == null || advices.size() == 0) {
                advices = new HashMap<>();
            }
            try {
                if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                    List<GPAdvice> before = advices.get("before");
                    if (before == null || before.size() == 0) {
                        before = new ArrayList<>();
                    }
                    before.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore()), config.getOrder()));
                    advices.put("before", before);
                }
                if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                    List<GPAdvice> after = advices.get("after");
                    if (after == null || after.size() == 0) {
                        after = new ArrayList<>();
                    }
                    after.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter()), config.getOrder()));
                    advices.put("after", after);
                }
                if (!(null == config.getAfterThrowing() || "".equals(config.getAfterThrowing()))) {
                    List<GPAdvice> afterThrowing = advices.get("afterThrowing");
                    if (afterThrowing == null || afterThrowing.size() == 0) {
                        afterThrowing = new ArrayList<>();
                    }
                    GPAdvice gpAdvice = new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAfterThrowing()), config.getOrder());
                    afterThrowing.add(gpAdvice);
                    advices.put("afterThrowing", afterThrowing);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            methodCache.put(declaredMethod, advices);

        }
    }

    public void init(GPAopConfig config, Class aspectClass, Map<String, Method> aspectMethods, String[] pointCut_method) {
        for (Method declaredMethod : this.targetClass.getDeclaredMethods()) {
            String methodString = declaredMethod.getName();
            if ("wait".equals(methodString) || "equals".equals(methodString) || "toString".equals(methodString) ||
                    "hashCode".equals(methodString) || "getClass".equals(methodString) || "notify".equals(methodString) ||
                    "notifyAll".equals(methodString)) {
                continue;
            }
            if (methodString.contains("throws")) {
                methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
            }
            if (check(pointCut_method,methodString)) {
                if (!check) {
                    check = true;
                }
                Map<String, List<GPAdvice>> advices = methodCache.get(declaredMethod);
                if (advices == null || advices.size() == 0) {
                    advices = new HashMap<>();
                }
                try {
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        List<GPAdvice> before = advices.get("before");
                        if (before == null || before.size() == 0) {
                            before = new ArrayList<>();
                        }
                        before.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore()), config.getOrder()));
                        advices.put("before", before);
                    }
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        List<GPAdvice> after = advices.get("after");
                        if (after == null || after.size() == 0) {
                            after = new ArrayList<>();
                        }
                        after.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter()), config.getOrder()));
                        advices.put("after", after);
                    }
                    if (!(null == config.getAfterThrowing() || "".equals(config.getAfterThrowing()))) {
                        List<GPAdvice> afterThrowing = advices.get("afterThrowing");
                        if (afterThrowing == null || afterThrowing.size() == 0) {
                            afterThrowing = new ArrayList<>();
                        }
                        GPAdvice gpAdvice = new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAfterThrowing()), config.getOrder());
                        afterThrowing.add(gpAdvice);
                        advices.put("afterThrowing", afterThrowing);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                methodCache.put(declaredMethod, advices);
            }

        }
    }
    public boolean check(String[] arr,String name){
        for (String s : arr) {
            if(s.equals(name)){
                return true;
            }
        }

        return false;
    }


    public void init(GPAopConfig config, Class aspectClass, Map<String, Method> aspectMethods, String pointCut_class[], String pointCut_method[]) {
        if (check(pointCut_class, targetClass.getSimpleName())) {
            for (Method declaredMethod : this.targetClass.getDeclaredMethods()) {
                String methodString = declaredMethod.getName();
                if ("wait".equals(methodString) || "equals".equals(methodString) || "toString".equals(methodString) ||
                        "hashCode".equals(methodString) || "getClass".equals(methodString) || "notify".equals(methodString) ||
                        "notifyAll".equals(methodString)) {
                    continue;
                }
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }
                if (check(pointCut_method, methodString)) {
                    if (!check) {
                        check = true;
                    }
                    Map<String, List<GPAdvice>> advices = methodCache.get(declaredMethod);
                    if (advices == null || advices.size() == 0) {
                        advices = new HashMap<>();
                    }
                    try {
                        if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                            List<GPAdvice> before = advices.get("before");
                            if (before == null || before.size() == 0) {
                                before = new ArrayList<>();
                            }
                            before.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore()), config.getOrder()));
                            advices.put("before", before);
                        }
                        if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                            List<GPAdvice> after = advices.get("after");
                            if (after == null || after.size() == 0) {
                                after = new ArrayList<>();
                            }
                            after.add(new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter()), config.getOrder()));
                            advices.put("after", after);
                        }
                        if (!(null == config.getAfterThrowing() || "".equals(config.getAfterThrowing()))) {
                            List<GPAdvice> afterThrowing = advices.get("afterThrowing");
                            if (afterThrowing == null || afterThrowing.size() == 0) {
                                afterThrowing = new ArrayList<>();
                            }
                            GPAdvice gpAdvice = new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAfterThrowing()), config.getOrder());
                            afterThrowing.add(gpAdvice);
                            advices.put("afterThrowing", afterThrowing);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    methodCache.put(declaredMethod, advices);
                }

            }
        }

    }

    public Map<String, List<GPAdvice>> getAdvices(Method method, Class targetClass) throws NoSuchMethodException {
        Map<String, List<GPAdvice>> adviceMap = methodCache.get(method);
        if (null == adviceMap) {
            Method declaredMethod = targetClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
            adviceMap = methodCache.get(declaredMethod);
            this.methodCache.put(declaredMethod, adviceMap);
        }
        return adviceMap;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }


}
